package com.qire.antsrouter.card;

import android.os.Handler;

import com.qire.antscore.entity.RouteMeta;
import com.qire.antsrouter.inject.Autowired;
import com.qire.antsrouter.inject.Injector;
import com.qire.antsrouter.utils.ClassUtils;
import com.qire.antsrouter.utils.StringUtils;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

/**
 * 基础跳卡
 * @param <V> 确定子类跳卡中保存的实际目标数据
 */
public abstract class Postcard<V> {

    public static final String CARD_KEY = "cardId";

    /** 运行时产生的跳卡ID，卡片ID全局唯一，用于标记获取比较卡片，现用于 Activity 跳转时从卡包里取出注入数据 */
    public final int cardId = IdGenerator.generator.create();
    /** 构建时间，用于卡包超时回收 */
    public final long buildTime = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());

    /** 注入数据容器 */
    protected final HashMap<String, Object> dataContainer = new HashMap();
    /** 路由元数据 */
    protected final RouteMeta routeMeta;
    /** handler 用于切换线程 */
    protected final Handler handler;

    protected int flags = -1;

    protected Postcard(RouteMeta routeMeta, Handler handler) {
        this.routeMeta  = routeMeta;
        this.handler    = handler;
    }

    /**
     * 获得跳卡分组信息。
     * @return
     */
    public String getGroup() {
        return routeMeta.getGroup();
    }

    /**
     * 获得跳卡的路径
     * @return
     */
    public String getPath() {
        return routeMeta.getPath();
    }

    /**
     * Intent.FLAG_ACTIVITY**
     * @param flag
     * @return
     */
    public Postcard withFlags(int flag) {
        this.flags = flag;
        return this;
    }

    /**
     * 注入参数，默认提供了通用的注入方式，扫描 {@link Autowired} 注解的字段来完成注入，如果有特殊需求可以覆盖重写这个方法。
     * <pre>
     * 如果 {@link Autowired} 提供了注入器，则使用提供的注入器完成注入；
     * 如果 {@link Autowired} 没有提供，则直接依据类型比较后 setter 方式注入，或反射方式注入
     * 注入规则 {@link Autowired#name() name} 存在则使用 {@link Autowired#name() name} 别名来匹配目标，否则直接比较字段名与类型
     * </pre>
     * @param destTarget 注入目标
     */
    public <T extends V> void inject(T destTarget) {
        // todo: 如果有必要可以提供一个 自动注入工厂类 来统一处理注入行为。讲这里的逻辑移交给注入工厂。提炼工具

        Class destCls = routeMeta != null ? routeMeta.getDestination() : destTarget.getClass();
        if(!destCls.isInstance(destTarget)) {
            return ;
        }

        Field[] fields = destCls.getDeclaredFields();
        HashMap<String, Injector> injectorMap = new HashMap<>();
        String defInjectorName = Injector.class.getCanonicalName();

        for(Field field : fields) {
            if(!field.isAnnotationPresent(Autowired.class)) {
                continue;
            }
            Autowired autowired = field.getAnnotation(Autowired.class);
            Class<? extends Injector> injectorClass = autowired.injector();
            String fieldName = StringUtils.getDef(autowired.name(), field.getName());
            String injectorName = injectorClass.getCanonicalName();

            Object data = dataContainer.get(fieldName);
            if(autowired.required() && data == null) {
                throw new RuntimeException("属性注入错误：'" + fieldName + "' 为空, 在打开 '" + destCls + "时，请检查是否有投递该字段!");
            }

            // 判断是否有提供注入器，如果没有则使用默认注入
            if(defInjectorName.equals(injectorName)) {
                if(data != null) {
                    ClassUtils.setFieldValue(field, destTarget, data);
                }
                continue;
            }

            // 如果有提供注入器，则使用提供的注入器注入。
            Injector injector = injectorMap.get(injectorName);
            if(null == injector) {
                injector = ClassUtils.newInstance(injectorClass);
                injectorMap.put(injectorName, injector);
            }
            injector.inject(field, this, destTarget);
        }
    }

    /**
     * 导航，查找到指定目标
     */
    public abstract <T extends Postcard> T navigation();

    /**
     * Id生成器，确保每一个跳卡的ID唯一
     */
    private final static class IdGenerator {

        private static final IdGenerator generator = new IdGenerator();

        private int baseId = 1;

        private IdGenerator() {}

        public synchronized int create(){
            return baseId++;
        }

    }

}
